package cn.yx.common.security.webmvc.filter;

import cn.hutool.core.util.StrUtil;
import cn.yx.common.core.enums.CharsetEnum;
import cn.yx.common.core.enums.ContentTypeEnum;
import cn.yx.common.core.exception.CommonException;
import cn.yx.common.core.pojo.ResponseVo;
import cn.yx.common.core.util.json.jackson.JsonUtil;
import cn.yx.common.redis.service.RedisService;
import cn.yx.common.security.constants.SecurityRedisCacheConstant;
import cn.yx.common.security.properties.SecurityProperties;
import cn.yx.common.security.webmvc.constant.message.VerifyCodeAuthenticationFilterMessageKey;
import cn.yx.common.security.webmvc.vo.in.VerifyCodeInVo;
import cn.yx.common.web.util.WebUtil;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.stereotype.Component;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;
import java.util.Objects;

/**
 * <p>普通验证码认证过滤器</p>
 *
 * @author Wgssmart
 */
@Component
public class VerifyCodeAuthenticationFilter extends BasicAuthenticationFilter {
    private final RedisService redisService;
    private final SecurityProperties securityProperties;

    public VerifyCodeAuthenticationFilter(AuthenticationManager authenticationManager, RedisService redisService, SecurityProperties securityProperties) {
        super(authenticationManager);
        this.redisService = redisService;
        this.securityProperties = securityProperties;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        if (StrUtil.equals(securityProperties.getVerifyCode().getLoginUrl(), request.getRequestURI())
                && StrUtil.equalsIgnoreCase(request.getMethod(), HttpMethod.POST.name())) {
            Cookie cookie = getSessionCookie(request);
            checkCode(request, response, cookie.getValue());
            return;
        }
        filterChain.doFilter(request, response);
    }

    /**
     * 获取会话cookie
     *
     * @param request
     * @return
     */
    private Cookie getSessionCookie(HttpServletRequest request) {
        Cookie[] cookies = request.getCookies();
        if (Objects.isNull(cookies)) {
            throw new CommonException(VerifyCodeAuthenticationFilterMessageKey.NO_SESSION_COOKIE);
        }
        Cookie cookie = Arrays.stream(request.getCookies())
                .filter(c -> securityProperties.getVerifyCode().getSessionName().equals(c.getName()))
                .findFirst().orElse(null);
        if (Objects.isNull(cookie)) {
            // @SimplifiedChinese=不存在Session Cookie
            // @English=No Session Cookie
            throw new CommonException(VerifyCodeAuthenticationFilterMessageKey.NO_SESSION_COOKIE);
        }
        return cookie;
    }

    /**
     * 校验验证码
     *
     * @param request
     * @param response
     * @param sessionId
     * @throws IOException
     */
    private void checkCode(HttpServletRequest request, HttpServletResponse response, String sessionId) throws IOException {
        VerifyCodeInVo verifyCodeInVo = WebUtil.getRequestData(request, VerifyCodeInVo.class);
        String verifyCode = verifyCodeInVo.getVerifyCode();
        String codeInRedis = (String) redisService.get(SecurityRedisCacheConstant.CODE_LOGIN_PREFIX + sessionId);
        response.setContentType(ContentTypeEnum.JSON.getValue());
        response.setCharacterEncoding(CharsetEnum.UTF8.getValue());
        if (StrUtil.isEmpty(codeInRedis)) {
            // 验证码不存在
            response.getWriter().write(JsonUtil.toJsonStr(
                    ResponseVo.authenticationError(VerifyCodeAuthenticationFilterMessageKey.CODE_NOTE_EXIST)));
            return;
        }
        if (!StrUtil.equals(verifyCode, codeInRedis)) {
            // 验证码错误
            response.getWriter().write(JsonUtil.toJsonStr(
                    ResponseVo.authenticationError(VerifyCodeAuthenticationFilterMessageKey.CODE_ERROR)));
        }
    }

}
